package map.tile.server.common;

import map.tile.server.dto.BoundingBox;
import map.tile.server.dto.TileIndex;

import java.util.ArrayList;
import java.util.List;

public class Translate {
    public static double EXTENDS_3857 = 20037508.3427892;
    public static double RADIO = EXTENDS_3857 / 180;


    /**
     * 根据经纬度和缩放等级，求得瓦片路径
     **/
    public static TileIndex getTileNumber(double lat, double lon, int zoom) {
        int xtile = (int) Math.floor((lon + 180) / 360 * (1 << zoom));
        int ytile = (int) Math.floor((1 - Math.log(Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))) / Math.PI) / 2 * (1 << zoom));
        if (xtile < 0)
            xtile = 0;
        if (xtile >= (1 << zoom))
            xtile = ((1 << zoom) - 1);
        if (ytile < 0)
            ytile = 0;
        if (ytile >= (1 << zoom))
            ytile = ((1 << zoom) - 1);

        return new TileIndex(zoom, xtile, ytile);
    }


    /**
     * 瓦片获得范围
     **/
    public static BoundingBox tile2boundingBox(final int x, final int y, final int zoom) {
        BoundingBox bb = new BoundingBox();
        bb.setLatMax(tile2lat(y, zoom));
        bb.setLatMin(tile2lat(y + 1, zoom));
        bb.setLonMin(tile2lon(x, zoom));
        bb.setLonMax(tile2lon(x + 1, zoom));
        return bb;
    }

    /**
     * 瓦片获得范围
     **/
    public static BoundingBox tile2boundingBox(TileIndex tileIndex) {
        BoundingBox bb = new BoundingBox();
        bb.setLatMax(tile2lat(tileIndex.getY(), tileIndex.getZ()));
        bb.setLatMin(tile2lat(tileIndex.getY() + 1, tileIndex.getZ()));
        bb.setLonMin(tile2lon(tileIndex.getX(), tileIndex.getZ()));
        bb.setLonMax(tile2lon(tileIndex.getX() + 1, tileIndex.getZ()));
        return bb;
    }

    /**
     * 瓦片转换经度
     **/
    public static double tile2lon(long x, int z) {
        return x / Math.pow(2.0, z) * 360.0 - 180;
    }

    /**
     * 瓦片转换纬度
     *
     * @param y
     * @param z
     * @return
     * @author 2018年8月13日 下午7:44:08
     * @modify {原因} by  2018年8月13日 下午7:44:08
     */
    public static double tile2lat(long y, int z) {
        double n = Math.PI - (2.0 * Math.PI * y) / Math.pow(2.0, z);
        return Math.toDegrees(Math.atan(Math.sinh(n)));
    }

    /**
     * 经纬度转WGS84坐标系
     *
     * @param lat 纬度
     * @param lon 经度
     * @return WGS84坐标
     */
    public static double[] Wgs84ToMector(double lat, double lon) {
        double x = lon * RADIO;
        double y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / Math.PI * 180;
        y = y * RADIO;
        return new double[]{x, y};
    }


    public static List<TileIndex> GetTMSCoord(BoundingBox boundingBox, Integer level, Integer type) {
        double[] pointMin = Wgs84ToMector(boundingBox.getLatMin(), boundingBox.getLonMin());
        double[] pointMax = Wgs84ToMector(boundingBox.getLatMax(), boundingBox.getLonMax());
        return GetTMSCoord(pointMin[0], pointMin[1], pointMax[0], pointMax[1], level, type);
    }
    public static List<TileIndex> GetTMSCoordMector(BoundingBox boundingBox, Integer level, Integer type) {
        return GetTMSCoord(boundingBox.getLonMin(), boundingBox.getLatMin(), boundingBox.getLonMax(), boundingBox.getLatMax(), level, type);
    }

    public static List<TileIndex> GetTMSCoord(double xMin, double yMin, double xMax, double yMax, Integer level, Integer type) {
        List<TileIndex> array = new ArrayList<>();
        double tile_size = EXTENDS_3857 / Math.pow(2, level - 1);//当前等级瓦片坐标宽度（宽高相等）
        Integer x1 =(int) Math.ceil((xMin + EXTENDS_3857) / tile_size) - 1;//起始X,减1是因为从0开始
        Integer x2 = (int)Math.ceil((xMax + EXTENDS_3857) / tile_size) - 1;//终止X

        Integer y1 = 0, y2 = 0;

        switch (type) {
            case 0:
                //谷歌地图TMSy值计算
                y1 = (int)Math.ceil((EXTENDS_3857 - yMax) / tile_size) - 1;//起始y
                y2 = (int)Math.ceil((EXTENDS_3857 - yMin) / tile_size) - 1;//终止y
                break;
            case 1:
                //标准TMSy值计算
                y1 = (int)Math.ceil((yMin + EXTENDS_3857) / tile_size) - 1;//起始y
                y2 = (int)Math.ceil((yMax + EXTENDS_3857) / tile_size) - 1;//终止y
                break;
            default:
                break;
        }
        //超出范围异常计算
        if ((x2 - x1) * (y2 - y1) > 5000000) {
            return array;
        }
        //防止计算产生负值
        if (x1 < 0) {
            x1 = 0;
        }
        if (x2 < 0) {
            x2 = 0;
        }
        if (y1 < 0) {
            y1 = 0;
        }
        if (y2 < 0) {
            y2 = 0;
        }
        for (Integer j = x1; j <= x2; j++) {
            for (Integer k = y1; k <= y2; k++) {
                array.add(new TileIndex(level, j, k));
            }
        }

        return array;
    }

}
